package com.ifd.gui;

import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;

import javax.swing.Action;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.text.BadLocationException;
import javax.swing.text.Caret;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import javax.swing.text.Keymap;
import javax.swing.text.TextAction;

public class JTextAreaOverwritable extends JTextArea {
	private Caret overwriteCaret;
	private Caret insertCaret;

	private static boolean overwriting = true;

	public static final String toggleOverwriteAction = "toggle-overwrite";

	private static Keymap sharedKeymap;

	private static final String keymapName = "OverwriteMap";

	private static final Action[] defaultActions = { new ToggleOverwriteAction() };

	private static JTextComponent.KeyBinding[] bindings = { new JTextComponent.KeyBinding(
			KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, 0),
			toggleOverwriteAction) };

	public JTextAreaOverwritable() {
		init();
	}

	public JTextAreaOverwritable(String text) {
		super(text);
		init();
	}

	public JTextAreaOverwritable(Document doc) {
		super(doc);
		init();
	}

	public JTextAreaOverwritable(int rows, int columns) {
		super(rows, columns);
		init();
	}

	public JTextAreaOverwritable(String text, int rows, int columns) {
		super(text, rows, columns);
		init();
	}

	public JTextAreaOverwritable(Document doc, String text, int rows,
			int columns) {
		super(doc, text, rows, columns);
		init();
	}

	private void init() {
	    overwriteCaret = new OverwriteCaret();
	    super.setCaret(overwriting ? overwriteCaret : insertCaret);
	}
	  public void setKeymap(Keymap map) {
		    if (map == null) {
		      super.setKeymap(null);
		      sharedKeymap = null;
		      return;
		    }

		    if (getKeymap() == null) {
		      if (sharedKeymap == null) {
		        // Switch keymaps. Add extra bindings.
		        removeKeymap(keymapName);
		        sharedKeymap = addKeymap(keymapName, map);
		        loadKeymap(sharedKeymap, bindings, defaultActions);
		      }
		      map = sharedKeymap;
		    }
		    super.setKeymap(map);
		  }

		  public void replaceSelection(String content) {
		    Document doc = getDocument();
		    if (doc != null) {
		      // If we are not overwriting, just do the
		      // usual insert. Also, if there is a selection,
		      // just overwrite that (and that only).
		      if (overwriting == true && getSelectionStart() == getSelectionEnd()) {

		        // Overwrite and no selection. Remove
		        // the stretch that we will overwrite,
		        // then use the usual code to insert the
		        // new text.
		        int insertPosition = getCaretPosition();
		        int overwriteLength = doc.getLength() - insertPosition;
		        int length = content.length();

		        if (overwriteLength > length) {
		          overwriteLength = length;
		        }

		        // Remove the range being overwritten
		        try {
		          doc.remove(insertPosition, overwriteLength);
		        } catch (BadLocationException e) {
		          // Won't happen
		        }
		      }
		    }

		    super.replaceSelection(content);
		  }

		  // Change the global overwriting mode
		  public static void setOverwriting(boolean overwriting) {
			  JTextAreaOverwritable.overwriting = overwriting;
		  }

		  public static boolean isOverwriting() {
		    return overwriting;
		  }

		  // Configuration of the insert caret
		  public void setCaret(Caret caret) {
		    insertCaret = caret;
		  }

		  // Allow configuration of a new
		  // overwrite caret.
		  public void setOverwriteCaret(Caret caret) {
		    overwriteCaret = caret;
		  }

		  public Caret getOverwriteCaret() {
		    return overwriteCaret;
		  }

		  // Caret switching
		  public void processFocusEvent(FocusEvent evt) {
		    if (evt.getID() == FocusEvent.FOCUS_GAINED) {
		      selectCaret();
		    }
		    super.processFocusEvent(evt);
		  }

		  protected void selectCaret() {
		    // Select the appropriate caret for the
		    // current overwrite mode.
		    Caret newCaret = overwriting ? overwriteCaret : insertCaret;

		    if (newCaret != getCaret()) {
		      Caret caret = getCaret();
		      int mark = caret.getMark();
		      int dot = caret.getDot();
		      caret.setVisible(false);

		      super.setCaret(newCaret);

		      newCaret.setDot(mark);
		      newCaret.moveDot(dot);
		      newCaret.setVisible(true);
		    }
		  }
	
	public static class ToggleOverwriteAction extends TextAction {
	    ToggleOverwriteAction() {
	      super(toggleOverwriteAction);
	    }

	    public void actionPerformed(ActionEvent evt) {
	      JTextAreaOverwritable.setOverwriting(!JTextAreaOverwritable
	          .isOverwriting());
	      JTextComponent target = getFocusedComponent();
	      if (target instanceof JTextAreaOverwritable) {
	    	  JTextAreaOverwritable field = (JTextAreaOverwritable) target;
	        field.selectCaret();
	      }
	    }
	  }
}
